﻿using System;
using System.Collections.Generic;

namespace Gqq.CSharpindepth2.CSharpBasic
{

    #region 基本类

    public class Basic
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class Derived : Basic
    {
        public int Addtional { get; set; }
    }

    public class Derived2 : Basic
    {
        public string Addtional { get; set; }
    }

    public class Derived3 : Basic
    {
        public int Addtional { get; set; }
    }



    #endregion

    #region 逆变接口

    public interface IProcess<in T> where T : Basic
    {
        void Process(T t);
    }

    public class BasicProcess : IProcess<Basic>
    {
        public void Process(Basic t)
        {
            var info = t.Name;
            var id = t.Id;
        }
    }


    public class ProcessClass<T> : IProcess<T> where T : Basic
    {
        public void Process(T t)
        {
            t.Name += " modify by gqq";
        }
    }
    #endregion

    #region 协变接口

    //加上out，表示可以协变
    public interface IGroup<out T> : IEnumerable<T>
    {
        // 只有是返回值，才可以协变。
        //本来是返回子类的，就可以用父类来承接。
    }

    public class Group<T> : List<T>, IGroup<T>
    {

    }

    public interface IInfo<out T>
    {
        T GetInfo();
    }

    public class DerivedInfo : IInfo<Derived2>
    {
        public Derived2 GetInfo()
        {
            return new Derived2() {Id = 2, Name = "derived2", Addtional = "sss"};
        }
    }

    #endregion

    #region 逆变协变测试类

    /// <summary>
    /// 测试委托逆变和协变的类
    /// 返回值协变：用父类来承载子类
    /// 参数逆变：用子类来代替父类
    /// </summary>
    public class DelegateGenericClass
    {
        public delegate TResult GenericHandler<in T, out TResult>(T arg);

        private delegate Basic BasicDelegate();

        private delegate Derived DerivedDelegate();

        #region 委托中的逆变和协变

        //协变
        public static void TestCovariant()
        {
            //derivedDelegate表示一类方法，输入一个id，将返回一个Derived对象。
            //返回的任何一个Derived对象，都是一个Basic对象，因此我们认为是“返回值的协变”
            GenericHandler<int, Derived> derivedDelegate = delegate(int id)
                {
                    var derived = new Derived { Id = id, Name = string.Empty };
                    return derived;
                };
            GenericHandler<int, Basic> basicDelegate = derivedDelegate;
            var basic = basicDelegate(3);
            Console.WriteLine(basic.Id);

            #region 2、测试List是不支持协变的

            //List<Derived> lstDs = new List<Derived> {{new Derived {Id = 1, Name = "tt"}}, {new Derived {Id = 2, Name = "xxx"}}};
            //List<Basic> lstDb = new List<Basic> {{new Derived {Id = 1, Name = "tt"}}, {new Derived {Id = 2, Name = "xxx"}}};
            //lstDb = lstDs;

            #endregion

            //特别注意，以下情况下是不可以进行协变的，除非用泛型
            //            DerivedDelegate dd = delegate() { return new Derived(); };
            //            BasicDelegate bd=dd;
        }

        //一个方法，如果传入的参数应该是basic，我们给它传入一个derived对象，这是完全没有问题的。
        //对于一个用basic对象做参数的委托，如果用一个以derived对象为参数的委托去指向它。应该是没有任何问题的。
        private static int ContraviriantMethod(Basic bs)
        {
            Console.WriteLine("{0} {1}", bs.Id, bs.Name);
            return 0;
        }

        /// <summary>
        /// 测试逆变
        /// </summary>
        public static void TestContraviriant()
        {
            GenericHandler<Basic, int> ghb = ContraviriantMethod;
            GenericHandler<Derived, int> ghd = ghb;
            ghd(new Derived { Name = "dfdsf", Id = 4 });
        }

        #endregion

        #region 接口中的逆变和协变

        /// <summary>
        /// 父类的泛型接口可以承载子类的泛型接口
        /// </summary>
        public static void TestInterfaceCovriant()
        {
            //            IGroup<Derived> dg = new Group<Derived>();
            //            IGroup<Basic> bg = dg;

            //同样的，我们可以看看List
            //这样是不行的，因为List<T>不支持协变
            //            List<Derived> lstds = new List<Derived>();
            //            List<Basic> lstbs = lstds;
        }

        //
        /// <summary>
        /// 要求输入Basic的参数，结果输入了Derived参数，这个很合理
        /// </summary>
        public static void TestInterfaceContravriant()
        {
            //            Basic b = new Derived();
            IProcess<Basic> proB = new ProcessClass<Basic>();
            //就是逆变
            IProcess<Derived> proD = proB;
            var d = new Derived { Id = 6, Name = "info" };
            proD.Process(d);
            Console.WriteLine(d.Name);
        }


        #endregion

    }


    public class DelegateGenericClass2
    {
        //协变接口out\
        public delegate TOut Output<out TOut>();

        public delegate string Input<in TInput>(TInput t);

        public static string InputB(Basic b)
        {
            return b.Name;
        }

        public static string InputD(Derived b)
        {
            return b.Name;
        }

        public static Basic OutputB()
        {
            return new Basic { Id = 1, Name = "aaa" };
        }

        public static Derived OutputD()
        {
            return new Derived { Id = 1, Name = "aaa" };
        }

        public static void MyTest()
        {
            //这是一个返回值为basic的委托，并且可以返回值协变。因此，返回值为Derived的方法可以
            //添加到这个委托。
            Output<Basic> b = OutputD;

            //通过协变得到的唯一缺点就是Derived中定义的变量，在result中是无法访问的，因为result是basic的。
            var result = b();
            Input<Derived> bb = InputB;
        }
    }

    #endregion

}
